package cli

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"log"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"gitee.com/dark.H/go-remote-repl/datas"
	"gitee.com/dark.H/go-remote-repl/lpc"
	"github.com/Qingluan/Tunnel/tcp"
)

// type LocalCentry struct {
// }

// func (local *LocalCentry) RunningAPIListener(ln net.Listener) {
func RunningAPIListener(ln net.Listener) {
	log.Println("Running local service")
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
	lpc.PID = os.Getpid()
	go func(ln net.Listener, c chan os.Signal) {
		sig := <-c
		log.Printf("Caught signal %s: shutting down.", sig)
		ln.Close()
		os.Exit(0)
	}(ln, sigc)

	for {
		fd, err := ln.Accept()
		if err != nil {
			log.Fatal("Accept error: ", err)
		}
		go LocalApi(fd)
	}
}

func LocalApi(con net.Conn) {
	defer con.Close()
	deadline := time.Now().Add(20 * time.Second)
	con.SetReadDeadline(deadline)
	if data := datas.JsonRecv(con); data != nil {
		if oper, ok := data["Op"]; ok {
			switch oper {
			case "update":
				if datar, ok := data["data"]; ok {
					buf, _ := base64.StdEncoding.DecodeString(datar.(string))
					apiinfo := new(lpc.APIInfo)
					apiinfo.Flags = make(map[string]lpc.Flag)
					// datas := make(map[string]interface{})
					if err := json.Unmarshal(buf, apiinfo); err != nil {
						datas.JsonErr(err, con)
						return
					} else {
						log.Println("payload:", data)
						if name, ok := data["file"]; ok {
							uname := filepath.Join(os.TempDir(), filepath.Base(name.(string)))
							log.Println("Wait Sync...:", uname)
							fp, err := os.Create(uname)
							if err != nil {
								log.Println("sync error:", err)
							}
							defer fp.Close()
							tcp.Copy(fp, con)
						} else {
							if _, err := os.Stat(apiinfo.BinFilePath); err != nil {

								datas.JsonSend(datas.Data{
									"Code": false,
									"Msg":  "no such binfaile:" + apiinfo.BinFilePath,
								}, con)
							} else {
								lpc.APIDB[apiinfo.Name] = apiinfo

								datas.JsonSend(datas.Data{
									"Code": true,
								}, con)
							}
						}

					}

				} else {
					datas.JsonErr(lpc.ErrDictNotFound, con)
				}

			case "ls":
				buf, err := json.Marshal(&lpc.APIDB)
				if err != nil {
					datas.JsonErr(err, con)
					return
				}
				datas.JsonSend(datas.Data{
					"Code": true,
					"Data": buf,
				}, con)
			case "pid":
				datas.JsonSend(datas.Data{
					"Code": true,
					"data": lpc.PID,
				}, con)
			case "do":
				AutoRun(data, con)
			case "outer":
				if !lpc.IfOuterStart() {
					go StartOuterAPIServer()
					datas.JsonSend(datas.Data{
						"Code": true,
					}, con)
				} else {
					datas.JsonSend(datas.Data{
						"Code": false,
						"Msg":  "already Start!",
					}, con)
				}
			default:
				datas.JsonSend(datas.Data{
					"Code": true,
				}, con)
			}
		}
	}
}

func AutoRun(data datas.Data, conn net.Conn) {
	now := time.Now()
	name, ok := data["name"].(string)
	if !ok {
		datas.JsonErr(lpc.ErrDictNotFound, conn)
		return
	}
	argsS, ok := data["args"].(string)
	log.Println("recv:", argsS)
	if !ok {
		datas.JsonErr(errors.New("no args"), conn)
		return
	}
	if m, ok := lpc.APIDB[name]; ok {
		m.StartPiepTo(argsS, conn)
		datas.JsonSend(datas.Data{
			"Code": true,
			"Used": time.Now().Sub(now),
		}, conn)

	} else {
		datas.JsonErr(errors.New("api:"+name+" not regist in this api centry!"), conn)
	}
	return
}
